# -*- coding: utf-8 -*-
"""
Module Description:发送http请求的模块
Date: 2016/2/15
Author:Chen wh, Bai Jin Ping
"""
from functools import partial

import requests
import urllib
from requests.exceptions import RequestException, TooManyRedirects, \
    ReadTimeout, ConnectTimeout, ConnectionError, HTTPError
from requests.adapters import HTTPAdapter
from common import service_config
from utils.traceback_utils import print_exc
from framework.core.context import get_trace_id
from framework.firefly_dc import runtime_logger as log


class HttpContentType(object):
    Json = 'application/json; charset=utf-8'
    Form = 'application/x-www-form-urlencoded'


class ClientSessionConn(object):
    _session = None

    @classmethod
    def init(cls):
        """
        初始化连接
        """
        # 连接池个数
        pool_connections = service_config.config.get('client_pool_connections', 16)
        # 单个连接池能够存储的个数，用于多线程环境
        pool_maxsize = service_config.config.get('client_pool_maxsize', 32)

        # requests.session底层使用urllib3.connectionpool.HTTPConnectionPool, 线程安全
        # http://urllib3.readthedocs.io/en/latest/reference/index.html#module-urllib3.connectionpool
        cls._session = requests.Session()
        cls._session.mount('http://', HTTPAdapter(pool_connections=pool_connections,
                                                  pool_maxsize=pool_maxsize))

    @classmethod
    def get_conn(cls):
        if not cls._session:
            cls.init()
        return cls._session

    @staticmethod
    def get_retry_times():
        return service_config.config.get('rpc_exception_retry_times', 0)

    @classmethod
    def request_form(cls, method, url, data, timeout, retry_exception=ConnectionError):
        retry_times = cls.get_retry_times()
        for i in xrange(retry_times + 1):
            try:
                response = cls.get_conn().request(
                    method=method,
                    url=url,
                    data=urllib.urlencode(data),  # 请求内容类型 form
                    headers={
                        'User-Agent': 'Game Server',
                        'Content-Type': HttpContentType.Form
                    },
                    timeout=timeout
                )
                return response
            except retry_exception as e:
                # 第一次失败，记录日志
                if i == 0:
                    log.error(str({"method": "request_form", "exception": e.message}))
                # 哨兵模式，最后一次异常抛出
                if i == retry_times:
                    raise e
                continue

    @classmethod
    def request_default(cls, method, url, data, content_type, timeout, retry_exception=ConnectionError):
        retry_times = cls.get_retry_times()
        for i in xrange(retry_times + 1):
            try:
                # 默认请求方式  HttpContentType.Json
                response = cls.get_conn().request(
                    method=method,
                    url=url,
                    json=data,  # 请求内容类型 json
                    headers={
                        'User-Agent': 'Game Server',
                        'Content-Type': content_type,
                        'Trace-Id': str(get_trace_id())
                    },
                    timeout=timeout
                )
                return response
            except retry_exception as e:
                # 第一次失败，记录日志
                if i == 0:
                    log.error(str({"method": "request_default", "exception": e.message}))
                # 哨兵模式，最后一次异常抛出
                if i == retry_times:
                    raise e
                continue


def http_req(url, data, callback, err_callback=None, method='POST', timeout=5, content_type=HttpContentType.Json):
    """
    向指定url发送HTTP请求
    :param url: 指定url
    :param data: 待发送的数据，推荐传入dict
    :param callback: 正常返回的回调函数
    :param err_callback: 发生错误时的回调函数
    :param method: POST or GET
    :param timeout:
    :param content_type: 请求内容类型
    :return: http response str
    """
    if method != 'POST' and method != 'GET':
        raise ValueError('don\'ot support HTTP method: %s' % method)

    url = str(url)
    err_msg = {'url': url, 'data': data}
    response = ''
    try:
        if content_type == HttpContentType.Form:
            response = ClientSessionConn.request_form(method=method, url=url, data=data, timeout=timeout)
        else:
            # 默认请求方式  HttpContentType.Json
            response = ClientSessionConn.request_default(method=method, url=url, data=data, content_type=content_type,
                                                         timeout=timeout)

    # TooManyRedirects: 太多跳转 ReadTimeout: 服务端未在时间内返回数据
    # ConnectTimeout: 连接服务端超时 ConnectionError: 连接错误, 比如dns，服务端拒绝连接
    # HTTPError: 未成功的状态码 200
    except (TooManyRedirects, ReadTimeout, ConnectTimeout, ConnectionError,
            HTTPError, RequestException) as re:
        err_msg.update({'raise': re.__class__.__name__, 'raise_msg': str(re)})
        if err_callback:
            log.error(str(err_msg))
            return err_callback(re)
        else:
            print_exc(err_msg)

    # 其它错误
    except Exception as re:
        err_msg.update({'raise': 'OtherError', 'raise_msg': str(re)})
        if err_callback:
            log.error(str(err_msg))
            return err_callback(re)
        else:
            print_exc(err_msg)

    # 验证http状态码
    try:
        # 判断状态码
        if response.status_code != 200:
            err_msg.update({'raise': 'StatusCodeError', 'status_code': response.status_code})
            log.error(str(err_msg))

    # 字符类型
    except AttributeError as re:
        if response != '':
            err_msg.update({'raise': 'AttributeError', 'msg': response})
            if err_callback:
                log.error(str(err_msg))
                return err_callback(re)
            else:
                print_exc(err_msg)

    response = response if not response else response.text
    if callback:
        # 正常回调
        return callback(response)
    else:
        return response


http_get = partial(http_req, method='GET')


def _test():
    d = {
        # 游戏编号
        'GameId': 101,
        'UserId': 7181,
        'ServerId': 1,
        'RoleName': '王爷聂琴未'
    }

    def cb(res):
        print 'success', res
        return res

    def ecb(err):
        print 'error', err
        return err

    print http_req('http://localhost:8000/', d, callback=cb, err_callback=ecb)


if __name__ == '__main__':
    _test()
